<?php

declare(strict_types=1);

namespace sheep\library;

use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use PhpZip\Exception\ZipException;
use PhpZip\ZipFile;
use sheep\exception\SheepException;

class FileOperator
{

    /**
     * @var string|null path prefix
     */
    protected $pathPrefix;


    public function __construct($root)
    {
        $root = is_link($root) ? realpath($root) : $root;
        if (!is_dir($root) || !is_readable($root)) {
            throw new SheepException('The root path ' . $root . ' is not readable.');
        }

        $this->setPathPrefix($root);
    }



    /**
     * 获取目录
     * @param string $origin_dirname  目录
     * @param bool $is_full_path  是否完整路径
     * @return array
     */
    public function getDirs($origin_dirname, $is_full_path = true)
    {
        $dirname = $this->applyPathPrefix($origin_dirname);
        if (!is_dir($dirname)) {
            throw new SheepException($dirname . ' is not a dir');
        }
        $files = $this->recursiveIterator($dirname);

        $list = [];
        foreach ($files as $file) {
            $normalized = [
                'type' => $file->getType(),
                'path' => $is_full_path ? $file->getPathname() : str_replace($this->pathPrefix, '', $file->getPathname()),
            ];

            // $normalized['timestamp'] = $file->getMTime();

            // if ($normalized['type'] === 'file') {
            //     $normalized['size'] = $file->getSize();
            // }

            $list[] = $normalized;
        }
        
        return $list;
    }



    /**
     * 删除目录
     * @param string $origin_dirname  目录
     * @param bool   $with_self 是否删除自身
     * @return boolean
     */
    public function rmDirs($origin_dirname, $with_self = true)
    {
        $dirname = $this->applyPathPrefix($origin_dirname);
        if (!is_dir($dirname)) {
            return false;
        }

        $files = $this->recursiveIterator($dirname, RecursiveIteratorIterator::CHILD_FIRST);

        foreach ($files as $fileinfo) {
            $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
            $todo($fileinfo->getRealPath());
        }
        if ($with_self) {
            @rmdir($dirname);
        }
        return true;
    }




    /**
     * 复制目录
     * @param string $source 源文件夹
     * @param string $dest   目标文件夹
     */
    public function copyDirs($origin_source, $origin_dest)
    {
        $source = $this->applyPathPrefix($origin_source);
        $dest = $this->applyPathPrefix($origin_dest);

        if (!is_dir($dest)) {
            @mkdir($dest, 0755, true);
        }

        $files = $this->recursiveIterator($source);

        foreach ($files as $file) {
            $current_path = str_replace($source, '', $file->getPathname());

            if ($file->isDir()) {
                $sontDir = $dest . $current_path;
                if (!is_dir($sontDir)) {
                   @mkdir($sontDir, 0755, true);
                }
            } else {
                copy($file->getPathname(), $dest . $current_path);
            }
        }
    }


    /**
     * 拷贝指定文件到目标文件夹
     * @param array $files 要拷贝的文件数组
     * @param string $dest   目标文件夹
     * @param string $files_dir   被拷贝文件的目录
     * @param bool $is_compress   是否压缩
     */
    public function copyAppointFiles($files, $origin_dest, $files_dir, $is_compress = false)
    {
        $dest = $this->applyPathPrefix($origin_dest);
        if (!is_dir($dest)) {
            @mkdir($dest, 0755, true);
        }

        $groupFiles = [
            'dir' => [],
            'file' => [],
        ];
        foreach($files as $file) {
            if ($file['type'] == 'dir') {
                $groupFiles['dir'][] = $file['path'];
            }
            if ($file['type'] == 'file') {
                $groupFiles['file'][] = $file['path'];
            }
        } 

        foreach($groupFiles['dir'] as $dir) {
            $dest_dir = $dest . $dir;
            if (!is_dir($dest_dir)) {
                @mkdir($dest_dir, 0755, true);
            }
        }

        foreach($groupFiles['file'] as $file) {
            $full_file = $this->applyPathPrefix($file);
            if (is_file($full_file)) {
                copy($full_file, ($dest . str_replace($files_dir, '', $file)));
            }
        }
        
        if (!$is_compress) {
            return $dest;       // 备份文件夹
        }

        $zipFile = new ZipFile();
        $zipPath = rtrim($dest, DIRECTORY_SEPARATOR) . '-' . date('Y-m-d') . '.zip';
        try {
            $zipFile
                ->addDirRecursive($dest)
                ->saveAsFile($zipPath)
                ->close();

            $this->rmDirs($origin_dest, true);
        } catch (ZipException $e) {
        } finally {
            $zipFile->close();
        }

        return $zipPath;
    }


    /**
     * 拷贝文件
     *
     * @return void
     */
    public function copy($origin_file, $origin_dest)
    {
        $file = $this->applyPathPrefix($origin_file);
        $dest = $this->applyPathPrefix($origin_dest);
        $full_dir = $dest . dirname($origin_file);

        if (!is_dir($full_dir)) {           // 防止子目录不存在，这里用除了文件名之外的完整路径
            @mkdir($full_dir, 0755, true);
        }

        copy($file, $dest . $origin_file);
    }


    /**
     * 移除文件
     *
     * @return void
     */
    public function rm($origin_file)
    {
        $file = $this->applyPathPrefix($origin_file);

        if (is_file($file)) {
            @unlink($file);
        }
    }



    /**
     * 修改文件
     *
     * @return void
     */
    public function modifyFile($origin_file, $start_search, $end_search) 
    {
        $file = $this->applyPathPrefix($origin_file);
        $content = file_get_contents($file);

        while(strpos($content, $start_search) !== false) {
            $remove_code = str_before(str_after($content, $start_search), $end_search);

            $content = str_replace($remove_code, '', $content);     // 移除标识内的代码
            $content = substr_replace($content, '', strpos($content, $start_search), strlen($start_search));        // 移除标识开始符
            $content = substr_replace($content, '', strpos($content, $end_search), strlen($end_search));            // 移除标识结尾符
        }

        file_put_contents($file, $content);
    }


    /**
     * 移除特定代码
     *
     * @return void
     */
    public function modifyTagFile($origin_file, $tags) 
    {
        $file = $this->applyPathPrefix($origin_file);
        $content = file_get_contents($file);

        $content = str_replace($tags, '', $content);

        file_put_contents($file, $content);
    }


    /**
     * 拼接前缀.
     *
     * @param string $path
     *
     * @return string prefixed path
     */
    protected function applyPathPrefix($path)
    {
        return $this->getPathPrefix() . ltrim($path, '\\/');
    }


    /**
     * 路径前缀
     *
     * @param string $prefix
     *
     * @return void
     */
    protected function setPathPrefix($prefix)
    {
        $prefix = (string) $prefix;

        if ($prefix === '') {
            $this->pathPrefix = null;

            return;
        }

        $this->pathPrefix = rtrim($prefix, '\\/') . DIRECTORY_SEPARATOR;
    }


    /**
     * 获取前缀
     *
     * @return string
     */
    protected function getPathPrefix()
    {
        return $this->pathPrefix;
    }

    /**
     * 迭代器
     * @param string $source 源文件夹
     * @param string $mode   迭代模式
     * @return RecursiveIteratorIterator
     */
    protected function recursiveIterator($source, $mode = RecursiveIteratorIterator::SELF_FIRST)
    {
        return new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
            $mode
        );
    }
}
